package yuque

import (
	"context"
	"errors"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"go-yuque/logger"
	"go-yuque/yuque/ele_handler"
	"go-yuque/yuque/entity"
	"go-yuque/yuque/util"
	"golang.org/x/net/html"
	"io"
	"strings"
)

func ParseBook(book *entity.YuqueBookStruct) (rootNodePt *entity.TocStruct, bookTreeStr string, rErr error) {
	rErr = nil

	if book == nil || book.Book.Toc == nil {
		return
	}

	// 知识库名称
	bookName := book.Book.Name
	if len(bookName) <= 0 {
		logger.Errorf("知识库名称为空")
		rErr = errors.New("知识库名称为空")
		return
	}

	// 判断知识库名称在输出目录是否已有目录和文件
	// stat, _ := os.Stat(filepath.Join(webEntity.GetTargetDir(), bookName))
	// if stat != nil && stat.IsDir() {
	// 	// 存在目录，查询目录下是否存在文件
	// 	files, _ := os.ReadDir(filepath.Join(webEntity.GetTargetDir(), bookName))
	// 	if len(files) > 0 {
	// 		logger.Errorf("知识库目录已存在并且目录不为空")
	// 		rErr = errors.New("知识库目录已存在并且目录不为空")
	// 		return
	// 	}
	// }

	rootNodePt = &entity.TocStruct{
		UUID:  "ROOT",
		Type:  "TITLE",
		Title: bookName,
	}

	tocs := book.Book.Toc

	// 将 toc 取引用放入切片
	tocPts := make([]*entity.TocStruct, 0, len(tocs))
	for _, toc := range tocs {
		tocPts = append(tocPts, &toc)
	}

	tocPts = append(tocPts, rootNodePt)

	// 将数组形式的文档组装成树结构
	tmpMap := make(map[string]*entity.TocStruct)
	// 先遍历一遍，把所有节点放入 map
	for _, toc := range tocPts {
		if len(toc.ParentUUID) <= 0 {
			// 根节点下的子节点
			toc.ParentUUID = "ROOT"
		}
		tmpMap[toc.UUID] = toc
	}

	// 组装树形结构
	for _, toc := range tocPts {
		assmeblyTree(toc, tmpMap)
	}

	logger.Debugf("组装树结构结束")

	countDocItem := 0
	countTitleItem := 0

	treeSb := strings.Builder{}
	printTree(rootNodePt, 0, &countDocItem, &countTitleItem, &treeSb)
	bookTreeStr = treeSb.String()

	logger.Infof("知识库:[%s]共有 %d 个目录, %d 个文档", bookName, countTitleItem, countDocItem)
	// logger.Infof("树形结构:\n%s", bookTreeStr)

	if book.Book.ItemsCount != countDocItem {
		errMsg := fmt.Sprintf("知识库:[%s]文档数量有误, 实际文档数量为 %d", bookName, book.Book.ItemsCount)
		logger.Errorf(errMsg)
		rErr = errors.New(errMsg)
		return
	}

	// tocs := book.Book.Toc
	// for _, toc := range tocs {
	// docTitle := toc.Title
	// }

	return
}

func assmeblyTree(toc *entity.TocStruct, tmpMap map[string]*entity.TocStruct) {

	// 找到这个节点的父节点
	if len(toc.ParentUUID) > 0 {
		if parentNode, ok := tmpMap[toc.ParentUUID]; ok && parentNode.ChildToc == nil && toc.UUID != parentNode.UUID {
			// 将这个父节点的子节点置为本节点
			parentNode.ChildToc = toc
		}

	}
	// 找到这个节点的子节点
	if len(toc.ChildUUID) > 0 {
		if childNode, ok := tmpMap[toc.ChildUUID]; ok {
			toc.ChildToc = childNode
		}
	}
	// 找到这个节点的兄节点
	if len(toc.PrevUUID) > 0 {
		if preNode, ok := tmpMap[toc.PrevUUID]; ok {
			toc.PreToc = preNode
		}
	}
	// 找到这个节点的弟节点
	if len(toc.SiblingUUID) > 0 {
		if siblingNode, ok := tmpMap[toc.SiblingUUID]; ok {
			toc.SiblingToc = siblingNode
		}
	}

}

func printTree(node *entity.TocStruct, level int, countDocItem, countTitleItem *int, w io.Writer) {
	if node == nil {
		return
	}
	if level == 0 {
		logger.Debugf("开始打印树形结构:")
		if w != nil {
			w.Write([]byte("开始打印树形结构:\n"))
		}
	}

	prefix := ""
	if level > 0 {
		prefix = fmt.Sprintf("%s ", strings.Repeat("--", level))
	}
	logger.Debugf("%sNode[%s] - %s", prefix, node.Title, node.Type)
	if w != nil {
		w.Write([]byte(fmt.Sprintf("%sNode[%s] - %s\n",
			prefix, node.Title, node.Type)))
	}
	if node.Type == "DOC" {
		*countDocItem += 1
	} else {
		*countTitleItem += 1
	}

	printTree(node.ChildToc, level+1, countDocItem, countTitleItem, w)

	printTree(node.SiblingToc, level, countDocItem, countTitleItem, w)
}

// ParseSingleDoc 解析单个文档
func ParseSingleDoc(ctxP context.Context, doc *entity.YuqueSingleDoc) (*strings.Builder, error) {
	if doc == nil {
		return nil, errors.New("文档为空")
	}

	if len(doc.Data.Content) <= 0 {
		return nil, errors.New("文档内容为空")
	}

	logger.Infof("[ParseSingleDoc]开始处理:[%s]", doc.Data.Title)

	reader := strings.NewReader(doc.Data.Content)
	document, err := goquery.NewDocumentFromReader(reader)
	if err != nil {
		logger.Errorf("content读取异常:%v", err)
		return nil, err
	}

	if document.Nodes != nil && len(document.Nodes) > 0 {
		result := &strings.Builder{}

		// 继承外部的 Context，应该含有 yqCookie
		ctx := context.WithValue(ctxP, "bookName", doc.Data.Title)
		ctx = context.WithValue(ctx, "result", &result)

		ctx = context.WithValue(ctx, ele_handler.DOWNLOAD_TOC_PATH_KEY, doc.DownloadTocPath)

		hook := ele_handler.HandleHook{
			DoAfterHandle: func(node *html.Node) error {
				err := recursionAllNodes(&ctx, node.FirstChild)
				if err != nil {
					return err
				}
				return nil
			},
			DoAfterPost: func(node *html.Node) error {
				err := recursionAllNodes(&ctx, node.NextSibling)
				if err != nil {
					return err
				}
				return nil
			},
		}
		ctx = context.WithValue(ctx, "hook", &hook)

		var ctxMap = make(map[string]any)
		ctx = context.WithValue(ctx, "ctxMap", ctxMap)

		handlerStack := util.NewSliceStack[*ele_handler.YuqueElementHandler]()
		ctx = context.WithValue(ctx, "handlerStack", handlerStack)

		err := recursionAllNodes(&ctx, document.Nodes[0])
		if err != nil {
			logger.Errorf("解析异常:%v", err)
			return nil, err
		}

		logger.Debugf("解析结果:%v", (result).String())
		return result, nil
	}

	return nil, nil
}

// 递归节点
func recursionAllNodes(ctx *context.Context, cNode *html.Node) error {

	if cNode == nil || ctx == nil {
		return nil
	}

	logger.Debugf("节点: %v %v", cNode.Type, cNode.Data)

	err := ele_handler.StartHandle(ctx, cNode)
	if err != nil {
		return err
	}

	return nil
}
